home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / bin / apport-cli < prev    next >
Text File  |  2008-10-24  |  10KB  |  293 lines

  1. #!/usr/bin/python
  2.  
  3. '''Command line Apport user interface.
  4.  
  5. Copyright (C) 2007 Canonical Ltd.
  6. Author: Michael Hofmann <mh21@piware.de>
  7.  
  8. This program is free software; you can redistribute it and/or modify it
  9. under the terms of the GNU General Public License as published by the
  10. Free Software Foundation; either version 2 of the License, or (at your
  11. option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
  12. the full text of the license.
  13. '''
  14.  
  15. # Web browser support:
  16. #    w3m, lynx: do not work
  17. #    elinks: works
  18.  
  19. import os.path, os, sys, subprocess, re, errno
  20. import tty, termios
  21. from datetime import datetime
  22.  
  23. try:
  24.     from gettext import gettext as _
  25.     import apport.ui
  26. except ImportError, e:
  27.     # this can happen while upgrading python packages
  28.     print >> sys.stderr, 'Could not import module, is a package upgrade in progress? Error:', e
  29.     sys.exit(1)
  30.  
  31. class CLIDialog:
  32.     '''Command line dialog wrapper.'''
  33.  
  34.     def __init__(self, heading, text):
  35.         self.heading = '\n*** ' + heading + '\n'
  36.         self.text = text
  37.         self.keys = []
  38.         self.buttons = []
  39.         self.visible = False
  40.  
  41.     def raw_input_char(self, text):
  42.         """ raw_input, but read only one character """
  43.  
  44.         sys.stdout.write(text + ' ')
  45.  
  46.         file = sys.stdin.fileno()
  47.         saved_attributes = termios.tcgetattr(file)
  48.         attributes = termios.tcgetattr(file)
  49.         attributes[3] = attributes[3] & ~(termios.ICANON)
  50.         attributes[6][termios.VMIN] = 1
  51.         attributes[6][termios.VTIME] = 0
  52.         termios.tcsetattr(file, termios.TCSANOW, attributes)
  53.  
  54.         try:
  55.             ch = str(sys.stdin.read(1))
  56.         finally:
  57.             termios.tcsetattr(file, termios.TCSANOW, saved_attributes)
  58.  
  59.         print
  60.         return ch
  61.  
  62.     def show(self):
  63.         self.visible = True
  64.         print self.heading
  65.         print self.text
  66.  
  67.     def run(self):
  68.         if not self.visible:
  69.             self.show()
  70.  
  71.         print
  72.         try:
  73.             # Only one button
  74.             if len (self.keys) <= 1:
  75.                 self.raw_input_char(_('Press any key to continue...'))
  76.                 return 0
  77.             # Multiple choices
  78.             while True:
  79.                 print _('What would you like to do? Your options are:')
  80.                 for index, button in enumerate(self.buttons):
  81.                     print '  %s: %s' % (self.keys[index], button)
  82.  
  83.                 response = self.raw_input_char(_('Please choose (%s):') % ('/'.join(self.keys)))
  84.                 try:
  85.                     return self.keys.index(response[0].upper()) + 1
  86.                 except ValueError:
  87.                     pass
  88.         except KeyboardInterrupt:
  89.             print
  90.             sys.exit(1)
  91.  
  92.     def addbutton(self, button):
  93.         self.keys.append(re.search('&(.)', button).group(1).upper())
  94.         self.buttons.append(re.sub('&', '', button))
  95.         return len(self.keys)
  96.  
  97.  
  98. class CLIProgressDialog(CLIDialog):
  99.     '''Command line progress dialog wrapper.'''
  100.  
  101.     def __init__(self, heading, text):
  102.         CLIDialog.__init__(self, heading, text)
  103.         self.progresscount = 0
  104.  
  105.     def set(self, progress = None):
  106.         self.progresscount = (self.progresscount + 1) % 5
  107.         if self.progresscount:
  108.             return
  109.  
  110.         if progress != None:
  111.             sys.stdout.write('\r%u%%' % (progress * 100))
  112.         else:
  113.             sys.stdout.write('.')
  114.         sys.stdout.flush()
  115.  
  116. class CLIUserInterface(apport.ui.UserInterface):
  117.     '''Command line Apport user interface'''
  118.  
  119.     def __init__(self):
  120.         apport.ui.UserInterface.__init__(self)
  121.  
  122.     #
  123.     # ui_* implementation of abstract UserInterface classes
  124.     #
  125.  
  126.     def ui_present_crash(self, desktop_entry):
  127.         date = datetime.strptime(self.report['Date'], '%a %b %d %H:%M:%S %Y')
  128.         # adapt dialog heading and label appropriately
  129.         if desktop_entry:
  130.             name = desktop_entry.getName()
  131.         elif self.report.has_key('ExecutablePath'):
  132.             name = os.path.basename(self.report['ExecutablePath'])
  133.         else:
  134.             name = self.cur_package
  135.         # translators: first %s: application name, second %s: date, third %s: time
  136.         heading = _('%s closed unexpectedly on %s at %s.') % (name, date.date(), date.time())
  137.  
  138.         dialog = CLIDialog(
  139.                 heading,
  140.                 _('If you were not doing anything confidential (entering passwords or other\n'
  141.                   'private information), you can help to improve the application by reporting\n'
  142.                   'the problem.'))
  143.         report = dialog.addbutton(_('&Report Problem...'))
  144.         ignore = dialog.addbutton(_('Cancel and &ignore future crashes of this program version'))
  145.         dialog.addbutton(_('&Cancel'))
  146.  
  147.         # show crash notification dialog
  148.         response = dialog.run()
  149.  
  150.         if response == report:
  151.             return {'action': 'report', 'blacklist': False}
  152.         if response == ignore:
  153.             return {'action': 'cancel', 'blacklist': True}
  154.         # Fallback
  155.         return {'action': 'cancel', 'blacklist': False}
  156.  
  157.     def ui_present_package_error(self):
  158.         name = self.report['Package']
  159.         dialog = CLIDialog(
  160.                 _('The package "%s" failed to install or upgrade.') % name,
  161.                 _('You can help the developers to fix the package by reporting the problem.'))
  162.         report = dialog.addbutton(_('&Report Problem...'))
  163.         dialog.addbutton(_('&Cancel'))
  164.  
  165.         response = dialog.run()
  166.  
  167.         if response == report:
  168.             return 'report'
  169.         # Fallback
  170.         return 'cancel'
  171.  
  172.     def ui_present_kernel_error(self):
  173.         dialog = CLIDialog (
  174.                 _('The kernel encountered a serious problem'),
  175.                 _('Your system might become unstable now and might need to be restarted.\n'
  176.                   'You can help the developers to fix the problem by reporting it.'))
  177.         report = dialog.addbutton(_('&Report Problem...'))
  178.         dialog.addbutton(_('&Cancel'))
  179.  
  180.         response = dialog.run()
  181.  
  182.         if response == report:
  183.             return 'report'
  184.         # Fallback
  185.         return 'cancel'
  186.  
  187.     def ui_present_report_details(self):
  188.         dialog = CLIDialog (
  189.                 _('Send problem report to the developers?'),
  190.                 _('After the problem report has been sent, please fill out the form in the\n'
  191.                   'automatically opened web browser.'))
  192.  
  193.         # report contents
  194.         details = ''
  195.         for key in self.report:
  196.             details += key + ':'
  197.             # string value
  198.             if not hasattr(self.report[key], 'gzipvalue') and \
  199.                 hasattr(self.report[key], 'isspace') and \
  200.                 not self.report._is_binary(self.report[key]):
  201.                 lines = self.report[key].splitlines()
  202.                 if len(lines) <= 1:
  203.                     details += ' ' + self.report[key] + '\n'
  204.                 else:
  205.                     details += '\n'
  206.                     for line in lines:
  207.                         details += '  ' + line + '\n'
  208.             else:
  209.                 details += ' ' + _('(binary data)') + '\n'
  210.  
  211.         # complete/reduced reports
  212.         if self.report.has_key('CoreDump') and self.report.has_useful_stacktrace():
  213.             complete = dialog.addbutton(_('&Send complete report (recommended; %s)') %
  214.                     self.format_filesize(self.get_complete_size()))
  215.             reduced = dialog.addbutton(_('Send &reduced report (slow Internet connection; %s)') %
  216.                     self.format_filesize(self.get_reduced_size()))
  217.         else:
  218.             complete = dialog.addbutton(_('&Send report (%s)') %
  219.                     self.format_filesize(self.get_complete_size()))
  220.             reduced = None
  221.  
  222.         view = dialog.addbutton(_('&View report'))
  223.         save = dialog.addbutton(_('&Keep report file for sending later or copying to somewhere else'))
  224.         
  225.         dialog.addbutton(_('&Cancel'))
  226.  
  227.         while True:
  228.             response = dialog.run()
  229.  
  230.             if response == complete:
  231.                 return 'full'
  232.             if response == reduced:
  233.                 return 'reduced'
  234.             if response == view:
  235.                 try:
  236.                     subprocess.Popen(["/usr/bin/sensible-pager"],
  237.                             stdin=subprocess.PIPE,
  238.                             close_fds=True).communicate(details)
  239.                 except IOError, e:
  240.                     # ignore broken pipe (premature quit)
  241.                     if e.errno == errno.EPIPE:
  242.                         pass
  243.                     else:
  244.                         raise
  245.                 continue
  246.             if response == save:
  247.                 print _('Problem report file:'), self.report_file
  248.                 return 'cancel'
  249.  
  250.             # Fallback
  251.             return 'cancel'
  252.  
  253.     def ui_info_message(self, title, text):
  254.         dialog = CLIDialog(title, text)
  255.         dialog.addbutton(_('&Confirm'))
  256.         dialog.run()
  257.  
  258.     def ui_error_message(self, title, text):
  259.         dialog = CLIDialog(_('Error: %s') % title, text)
  260.         dialog.addbutton(_('&Confirm'))
  261.         dialog.run()
  262.  
  263.     def ui_start_info_collection_progress(self):
  264.         self.progress = CLIProgressDialog (
  265.                 _('Collecting problem information'),
  266.                 _('The collected information can be sent to the developers to improve the\n'
  267.                   'application. This might take a few minutes.'))
  268.         self.progress.show()
  269.  
  270.     def ui_pulse_info_collection_progress(self):
  271.         self.progress.set()
  272.  
  273.     def ui_stop_info_collection_progress(self):
  274.         print
  275.  
  276.     def ui_start_upload_progress(self):
  277.         self.progress = CLIProgressDialog (
  278.                 _('Uploading problem information'),
  279.                 _('The collected information is being sent to the bug tracking system.\n'
  280.                   'This might take a few minutes.'))
  281.         self.progress.show()
  282.  
  283.     def ui_set_upload_progress(self, progress):
  284.         self.progress.set(progress)
  285.  
  286.     def ui_stop_upload_progress(self):
  287.         print
  288.  
  289. if __name__ == '__main__':
  290.     app = CLIUserInterface()
  291.     if not app.run_argv():
  292.         print >> sys.stderr, _('No pending crash reports. Try --help for more information.')
  293.